﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;

namespace SerialPortGuiTermial
{
    public partial class MainForm : Form
    {
        string _port;
        int _baud = 0;
        bool _connected = false;
        
        public MainForm()
        {
            InitializeComponent();
            btnConnectDisconnect.Text = "Connect";
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            if (!int.TryParse(cmbBaud.Text, out _baud))
            {
                errorProvider1.SetError(cmbBaud, "Invalid baud rate");
                return;
            }

            errorProvider1.SetError(cmbBaud, "");

            
            _port = cmbPort.Text;

            try
            {
                if (!_connected)
                {
                    serialPort1.PortName = _port;
                    serialPort1.BaudRate = _baud;
                    serialPort1.RtsEnable = chkRtsEnable.Checked;
                    serialPort1.DtrEnable = chkDtrEnable.Checked;
                    serialPort1.Handshake = ((FlowControlItem)cmbFlowControl.SelectedItem).FlowControl;

                    serialPort1.Open();
                    lblStatus.Text = string.Format("Connected to {0} @ {1}", _port, _baud);
                    _connected = true;
                    setControlStates();
                }
                else
                {
                    serialPort1.Close();
                   
                    _connected = false;
                    setControlStates();
                }
            }
            catch(Exception ex)
            {
                showError(ex);
            }
        }

        private void setControlStates()
        {
            btnConnectDisconnect.Text = _connected ? "Disconnect" : "Connect";
            cmbPort.Enabled = !_connected;
            cmbBaud.Enabled = !_connected;
            btnRefreshPortList.Enabled = !_connected;
            cmbFlowControl.Enabled = !_connected;
            

            //txtRx.Enabled = _connected;
            txtTx.Enabled = _connected;
            //chkAutoScroll.Enabled = _connected;
            chkDtrEnable.Enabled = _connected;
            chkRtsEnable.Enabled = _connected;
        }

        private void showError(Exception ex)
        {
            string errMsg = "ERROR:" + ex.Message;
            lblStatus.Text = errMsg;
            MessageBox.Show(errMsg);
        }

        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (!this.InvokeRequired)
            {
                if(serialPort1.IsOpen)
                    appendText(serialPort1.ReadExisting());
                
                //Application.DoEvents();
            }
            else
            {
                this.BeginInvoke(new EventHandler<SerialDataReceivedEventArgs>(serialPort1_DataReceived)
                                    , sender, e);
            }
        }

        private void appendText(string str)
        {
            txtRx.AppendText(str);

            if (chkAutoScroll.Checked && txtRx.TextLength > 0)
            {
                txtRx.SelectionStart = txtRx.TextLength - 1;
                txtRx.ScrollToCaret();
            }
        }

        private void txtTx_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                try
                {
                    serialPort1.Write(txtTx.Text + "\r\n");
                    txtTx.Clear();
                }
                catch(Exception ex)
                {
                    showError(ex);
                }
            }
        }

        private void serialPort1_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
        {
            if (!this.InvokeRequired)
                appendText(string.Format("ERROR: {0}\n", e.EventType));
            else
                this.BeginInvoke(new EventHandler<SerialErrorReceivedEventArgs>(serialPort1_ErrorReceived)
                                    , sender, e);
        }

        private void Form1_Load(object sender, EventArgs e)
        {

            btnRefreshPortList_Click(this, EventArgs.Empty);

            int[] bauds = { 110, 300, 1200, 2400
                              , 4800, 9600, 19200, 38400
                              , 57600, 115200, 230400
                              , 460800, 921600 };

            foreach (var baud in bauds)
            {
                cmbBaud.Items.Add(baud);
            }

            if (cmbBaud.Items.Count > 1)
                cmbBaud.SelectedIndex = 0;

            cmbFlowControl.Items.Add(new FlowControlItem { Text = "None", FlowControl = Handshake.None });
            cmbFlowControl.Items.Add(new FlowControlItem { Text = "RTS", FlowControl = Handshake.RequestToSend });
            cmbFlowControl.Items.Add(new FlowControlItem { Text = "RTS Xon/Xoff", FlowControl = Handshake.RequestToSendXOnXOff });
            cmbFlowControl.Items.Add(new FlowControlItem { Text = "Xon/Xoff", FlowControl = Handshake.XOnXOff });
            cmbFlowControl.SelectedIndex = 0;

            //Control states
            setControlStates();
        }

        private void chkDtrEnable_CheckedChanged(object sender, EventArgs e)
        {
            var chk = (CheckBox)sender;
            serialPort1.DtrEnable = chk.Checked;
        }

        private void chkRtsEnable_CheckedChanged(object sender, EventArgs e)
        {
            var chk = (CheckBox)sender;
            serialPort1.RtsEnable = chk.Checked;
        }

        private void btnRefreshPortList_Click(object sender, EventArgs e)
        {
            try
            {
                cmbPort.Items.Clear();

                foreach (string port in SerialPort.GetPortNames())
                {
                    cmbPort.Items.Add(port);
                }

                if (cmbPort.Items.Count > 1)
                    cmbPort.SelectedIndex = 0;
            }
            catch (Exception ex)
            {
                showError(ex);
            }
        }

        private void clearToolStripMenuItem_Click(object sender, EventArgs e)
        {
            txtRx.Clear();
        }
    }

    class FlowControlItem
    {
        public string Text { get; set; }
        public Handshake FlowControl { get; set; }

        public override string ToString()
        {
            return Text;
        }
    }
}